import apiService from './service/apiService';
import signService from './service/signService';
import cookieService from './service/cookieService';
import commons from './commons/commons';

export default class WanSdk {
    constructor() {
        this.version = '1.0.24' // sdk版本
        this.isInit = false; // 是否初始化完成
        this.isDebug = 0; // 是否调试
        this.gameId = 0 // 游戏id
        this.channel = 2002 // 渠道id
        this.gameChannelId = 0 // 游戏渠道配置id
        this.packageId = 0 // 包id
        this.userInfo = {} // 用户信息
        this.deviceInfo = {} // 设备信息
        this.launchInfo = {} // 启动数据
        this.serverAppkey = 'a9330f4d727f9751eaea490e9311dda2' // 签名key
        this.serverHost = 'https://mtg-sdk-server.xingyi-games.com' // 请求服务器地址
        this.logHost = 'https://mtg-sdk-server.xingyi-games.com' // 上报日志服务器
        this.saveDeviceName = '#__sdkDeviceId__' // 存储设备唯一id
        this.deviceNum = '' // 自定义设备id
        this.deviceNumTime = 0 // 自定义设备id的创建时间
        this.firstupIsSend = 0 // 是否已经上报激活给后端
        this.postMessageCbList = {} // 异步通知父窗口回调函数列表
        this.roleLoginNum = 0 // 防止研发过渡调用登录上报，此处做下统计判断
        this.wxMiniOpenid = '' // 微信小程序用户openid
        this.wxOffiaccountOpenid = '' // 微信公众号用户openid
        this.toApkCacheId = '' // 转端cache id
    }

    // 是否初始化完成
    checkInit() {
        return this.isInit
    }

    // 是否登录完成
    checkLogin() {
        return this.userInfo.hasOwnProperty('userId')
    }

    // 打印调试信息
    printDebug(...args) {
        if (!this.isDebug == 1) {
            return
        }
        let now = commons.formatDate(new Date(), 'YmdHis.u')
        let params = Array.prototype.slice.call(args);
        console.log('[SDK DEBUG]['+now+']', JSON.stringify(params))
    }

    // callback
    cbRes(func, res) {
        this.printDebug('回调请求结果原样', res)
        func(res)
    }

    // callback true
    cbTrue(func, data = {}) {
        this.printDebug('回调成功结果', data)
        func({
            code: 0,
            msg: 'success',
            data: data
        })
    }

    // callback fail
    cbFail(func, errno, errmsg, data={}) {
        this.printDebug('回调失败结果', errno, errmsg)
        func({
            code: errno,
            msg: errmsg,
            data: data
        })
    }

    // 获得设备唯一id
    // localstorage存储是基于域名存储的，有可能出现一条域名多个游戏的情况，如
    // http://sdk.game.com/1001 和 http://sdk.game.com/1002
    // 所以我们判断此游戏是否已经缓存了设备信息时，要基于 gameid 作为索引去获取
    getDeviceNum(cb) {
        const _this = this
        var saveid = _this.saveDeviceName + _this.gameId
        var data = cookieService.getStorage(saveid)
        if (data) {
            data = JSON.parse(data)
            return _this.cbTrue(cb, data)
        } else {
            return _this.cbTrue(cb)
        }
    }

    // 设置设备唯一id
    setDeviceNum(data, cb = null) {
        let _this = this
        var saveid = _this.saveDeviceName + _this.gameId
        data = JSON.stringify(data)
        cookieService.setStorage(saveid, data)
        return _this.cbTrue(cb)
    }

    // 上报日志
    addLog(event_id, event_name, params) {
        const _this = this

        // 基础信息
        params.event_id = event_id
        params.event_name = event_name
        params.event_time = Math.floor(Date.now() / 1000)

        // 补充额外数据
        params = Object.assign(_this.getExtendParams(), params)

        // 对数据进行签名
        params.time = Math.floor(Date.now() / 1000)
        let sign_info = signService.createSign(params, this.serverAppkey)
        params.sign = sign_info.sign
        _this.printDebug('签名源串', sign_info.str)

        let url = this.logHost + '/api/h5/log'

        _this.printDebug('上报日志', url, params)

        return apiService.send(url, params, function(res){})
    }

    // 启动时的参数
    initStartParams() {
        let query = this.launchInfo.query
        if (query.hasOwnProperty('package_id')) {
            this.packageId = query.package_id
        }

        for (const key of ['device_type', 'imei', 'oaid', 'android_id', 'idfa', 'idfv', 'device_num', 'device_factory', 'device_os', 'device_system', 'device_screen_w', 'device_screen_h', 'package_name', 'sign_md5', 'net_work']) {
            if (query.hasOwnProperty(key)) {
                this.deviceInfo[key] = query[key]
            }
        }

        this.printDebug('接收到入口启动参数', query)

        return true
    }

    // 获得额外参数，用于请求时附带上
    getExtendParams() {
        const _this = this
        let params = {}

        // 基础信息
        params.user_id = _this.userInfo.userId
        params.channel_openid = _this.userInfo.channelOpenid
        params.game_id = _this.gameId
        params.channel = _this.channel
        params.game_channel_id = _this.gameChannelId
        params.package_id = _this.packageId
        params.sdk_version = _this.version

        // 设备信息
        params.device_num = _this.deviceNum
        params.device_num_time = _this.deviceNumTime
        for (const key of ['device_type', 'imei', 'oaid', 'android_id', 'idfa', 'idfv', 'device_factory', 'device_os', 'device_system', 'device_screen_w', 'device_screen_h', 'package_name', 'sign_md5', 'net_work']) {
            if (_this.deviceInfo.hasOwnProperty(key)) {
                params[key] = _this.deviceInfo[key]
            }
        }

        // 小程序启动参数
        params.launch_info = JSON.stringify(this.launchInfo)

        return params
    }

    send(api, params = {}, cb) {
        const _this = this

        // 补充额外数据
        params = Object.assign(_this.getExtendParams(), params)

        // 对数据进行签名
        params.time = Math.floor(Date.now() / 1000)
        let sign_info = signService.createSign(params, this.serverAppkey)
        params.sign = sign_info.sign
        _this.printDebug('签名源串', sign_info.str)

        let url = this.serverHost + api

        _this.printDebug('发起SDK请求', url, params)

        return apiService.send(url, params, cb)
    }

    // 上报激活
    sendFirstup(data) {
        let _this = this
        _this.printDebug('上报激活')
        _this.send('/api/h5/user/firstup', {}, function(res){
            if (res.code === 0) {
                data.is_send = 1
                _this.setDeviceNum(data)
                _this.firstupIsSend = 1
                _this.postMessage('firstupSend', data, function(resdata){})
            }
        })
    }

    firstup() {
        const _this = this

        // 读取数据存储，是否为首次启动，是则上报激活
        _this.getDeviceNum(function(res) {
            if (res.data.device_num) {
                _this.deviceNum = res.data.device_num
                _this.deviceNumTime = res.data.create_time
                _this.firstupIsSend = res.data.is_send
                // 如果已经有存储，但是未发送，则发送
                if (_this.firstupIsSend != 1) {
                    _this.sendFirstup(res.data)
                }

                _this.printDebug('获得设备唯一码信息', res)
            } else {
                // 如果父ifream传来了 device_num
                if (_this.deviceInfo.hasOwnProperty('device_num')) {
                    var device_num = _this.deviceInfo.device_num
                } else if (_this.wxOffiaccountOpenid.length > 5) {
                    // 如果有公众号用户openid，则拿openid作为唯一设备码
                    var device_num = _this.wxOffiaccountOpenid
                } else if (_this.wxMiniOpenid.length > 5) {
                    // 如果有小程序用户openid，则拿openid作为唯一设备码
                    var device_num = _this.wxMiniOpenid
                } else {
                    // 随机生成一个设备码
                    var device_num = commons.uniqid()
                }

                let data = {
                    device_num: device_num,
                    create_time: Math.floor(Date.now() / 1000),
                    is_send: 0
                }
                _this.setDeviceNum(data, function(res) {
                    _this.deviceNum = data.device_num
                    _this.deviceNumTime = data.create_time
                    _this.sendFirstup(data)

                    _this.printDebug('设置设备唯一码信息', res)
                })
            }
        })
    }

    // 向父窗口发送异步消息
    postMessage(event_name, params, cb) {
        const _this = this
        if (window.self === window.top) {
            this.printDebug('请在壳内使用 h5sdk', event_name)
            return;
        }

        const cbid = event_name + '-' + new Date().valueOf() + '' + Math.random();
        _this.postMessageCbList[cbid] = cb

        window.parent.postMessage({
            event_name: event_name,
            params: params,
            cbid: cbid
        }, "*");
    };

    // 接收到父窗口异步消息
    handleMessage(event_name, params, cbid) {
        const _this = this
        if (_this.postMessageCbList.hasOwnProperty(cbid)) {
            _this.postMessageCbList[cbid](params)
            delete _this.postMessageCbList[cbid]
        }
    }

    // 初始化
    init(params = {}, cb) {
        const _this = this
        if (_this.checkInit()) {
            return _this.cbFail(cb, 9999, '无需重复初始化')
        }

        for (const item of ['game_id']) {
            if (!params.hasOwnProperty(item)) {
                let msg = '缺少参数: '+item
                return _this.cbFail(cb, 9999, msg)
            }
        }

        _this.gameId = params.game_id
        _this.isDebug = params.debug || 0

        if (params.hasOwnProperty('server_host')) {
            _this.serverHost = params.server_host
        }
        if (params.hasOwnProperty('log_host')) {
            _this.logHost = params.log_host
        }

        // 监听父窗口的异步消息
        window.addEventListener('message', function (e) {
            const data = e.data;
            _this.printDebug('接收到异步消息', data)
            for (const item of ['event_name','params','cbid']) {
                if (!data.hasOwnProperty(item)) {
                    _this.printDebug('非我方sdk消息')
                    return
                }
            }
            _this.handleMessage(data.event_name, data.params, data.cbid);
        });

        _this.postMessage('init', {}, function(res){
            _this.printDebug('收到父类init数据', res)
            _this.launchInfo.query = JSON.parse(res.data.query)
            _this.initStartParams()

            _this.channel = res.data.channel ?? 2002
            _this.gameChannelId = res.data.game_channel_id ?? 0
            _this.toApkCacheId = res.data.toapk_cache_id ?? ''
            _this.wxOffiaccountOpenid = res.data.openid ?? ''
            _this.wxMiniOpenid = res.data.wx_openid ?? ''

            // 完成初始化后进行激活上报
            if (_this.toApkCacheId.length < 1) {
                _this.firstup()
            }

            _this.isInit = true;
            _this.addLog(1002, 'init', {})
            _this.printDebug('初始化成功', params)

            return _this.cbTrue(cb, _this.launchInfo)
        })
    }

    // 登录
    login(params = {}, cb) {
        const _this = this
        if (!_this.checkInit()) {
            return _this.cbFail(cb, 9999, '初始化未完成')
        }

        _this.postMessage('login', {}, function(res){
            if (res.code > 0) {
                _this.printDebug('登录失败', res.code, res.msg)
                return _this.cbFail(cb, res.code, res.msg)
            }

            var data = res.data
            _this.send('/api/h5/user/login', data, res => {
                if (res.code !== 0) {
                    _this.printDebug('登录失败', res)
                    return _this.cbFail(cb, res.code, res.msg)
                }
                _this.userId = res.data.user_id
                _this.userInfo.userId = res.data.user_id
                _this.userInfo.channelOpenid = res.data.channel_openid
                _this.userInfo.token = res.data.token
                _this.userInfo.isReg = res.data.is_reg

                _this.printDebug('登录成功', res)
                _this.postMessage('loginSuccess', res.data, function(resdata){})
                return _this.cbTrue(cb, res.data)
            })
        })
    }

    // 创建订单
    payCreate(params = {}, cb) {
        const _this = this

        if (!_this.checkLogin()) {
            return _this.cbFail(cb, 9999, '请先完成登录')
        }

        for (const item of ['game_trade_no','amount','goods_id','goods_name','server_id','server_name','role_id','role_name','role_level']) {
            if (!params.hasOwnProperty(item)) {
                let msg = '缺少参数: '+item
                return _this.cbFail(cb, 9999, msg)
            }
        }

        _this.send('/api/h5/pay/create', params, res => {
            _this.printDebug('创建订单返回数据', res)
            if (res.code !== 0) {
                return _this.cbFail(cb, res.code, res.msg)
            }
            _this.postMessage('pay', res.data, function(res){})

            return _this.cbTrue(cb, {trade_no: res.data.trade_no})
        })
    }

    // 转端
    h5ToApk(role_id) {
        const _this = this
        _this.postMessage('h5toapk', {
            user_id: _this.userId,
            role_id: role_id,
        }, function(res){})
    }

    // 角色注册
    roleReg(params, cb) {
        const _this = this
        if (!_this.checkLogin()) {
            _this.cbFail(cb, 9999, '请先登录')
            return
        }

        let api = '/api/h5/role/reg'
        _this.send(api, params, (res) => {
            _this.cbRes(cb, res)
        })

        _this.postMessage('roleReg', params, function(res){})
    }

    // 角色登录
    roleLogin(params, cb) {
        const _this = this
        if (!_this.checkLogin()) {
            _this.cbFail(cb, 9999, '请先登录')
            return
        }

        // 如果过渡频繁请求，则直接停掉
        if (_this.roleLoginNum > 2) {
            _this.cbFail(cb, 9999, '请求上报登录次数过多了')
            return
        }

        let api = '/api/h5/role/login'
        _this.send(api, params, (res) => {
            _this.roleLoginNum++
            _this.cbRes(cb, res)
        })

        // 判断转端
        _this.h5ToApk(params.role_id)
    }

    // 角色其他行为事件上报
    roleEvent(params, cb) {
        const _this = this
        if (!_this.checkLogin()) {
            _this.cbFail(cb, 9999, '请先登录')
            return
        }

        // 基础信息
        params.event_time = Math.floor(Date.now() / 1000)

        // 补充额外数据
        params = Object.assign(_this.getExtendParams(), params)

        // 对数据进行签名
        params.time = Math.floor(Date.now() / 1000)
        let sign_info = signService.createSign(params, this.serverAppkey)
        params.sign = sign_info.sign
        _this.printDebug('签名源串', sign_info.str)

        let url = this.logHost + '/api/game/roleevent'

        _this.printDebug('上报日志', url, params)

        return apiService.send(url, params, cb)
    }

    // 激励广告-加载广告
    adviewLoad(params, cb) {
        const _this = this
        if (!_this.checkLogin()) {
            _this.cbFail(cb, 9999, '请先登录')
            return
        }

        for (const item of ['reward_name','reward_amount','orientation']) {
            if (!params.hasOwnProperty(item)) {
                let msg = '缺少参数: '+item
                return _this.cbFail(cb, 9999, msg)
            }
        }

        params.user_id = _this.userId
        _this.postMessage('adviewLoad', params, function(res){
            if (res.code > 0) {
                _this.cbFail(cb, res.code, res.msg)
            } else {
                _this.cbTrue(cb, res.data)
            }
        })
    }

    // 激励广告-播放广告
    adviewShow(params, cb) {
        const _this = this
        if (!_this.checkLogin()) {
            _this.cbFail(cb, 9999, '请先登录')
            return
        }

        params.user_id = _this.userId
        _this.postMessage('adviewShow', params, function(res){
            if (res.code > 0) {
                _this.cbFail(cb, res.code, res.msg)
            } else {
                _this.cbTrue(cb, res.data)
            }
        })
    }
}
